
// Copyright (c) 2010-2021 niXman (github dot nixman at pm dot me). All
// rights reserved.
//
// This file is part of YAS(https://github.com/niXman/yas) project.
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
//
//
// Boost Software License - Version 1.0 - August 17th, 2003
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

#ifndef __yas__detail__tools__json_tools_hpp
#define __yas__detail__tools__json_tools_hpp

#include <yas/detail/io/serialization_exceptions.hpp>

#include <cstdint>

namespace yas {
    namespace detail {

        /***************************************************************************/
        // 跳过whitespace
        template<typename Archive>
        void json_skipws(Archive& ar) {
            while (true) {
                const char ch = ar.peekch();
                switch (ch) {
                case ' ':
                case '\n':
                case '\t':
                case '\r': { ar.getch(); break; }
                default: return;
                }
            }
        }

        /***************************************************************************/
        // 跳过数字
        template<typename Archive>
        void json_skip_num(Archive& ar) {
            while (true) {
                const char ch = ar.peekch();
                switch (ch) {
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                case '.':
                case 'e':
                case 'E': { ar.getch(); break; }
                default: return;
                }
            }
        }

        /***************************************************************************/
        // 跳过Unicode
        template<typename Archive>
        void json_skip_unicode(Archive& ar) {
            char buf[4];
            ar.read(buf, 4);
        }

        /***************************************************************************/
        // 跳过bool值：true/false
        template<typename Archive>
        void json_skip_bool(char ch, Archive& ar) {
            char buf[4];
            if (ch == 't') {
                ar.read(buf, 3);
            }
            else {
                ar.read(buf, 4);
            }
        }

        /***************************************************************************/
        // 跳过null
        template<typename Archive>
        void json_skip_null(Archive& ar) {
            char buf[3];
            ar.read(buf, 3);
        }

        /***************************************************************************/

        template<typename Archive>
        void json_skip_escapes(Archive& ar) {
            const char ch = ar.peekch();
            switch (ch) {
            case '\"':
            case '\\':
            case '/':
            case 'b':
            case 'f':
            case 'n':
            case 'r':
            case 't':
            default: { ar.getch(); break; }
            case 'u': return json_skip_unicode(ar);
            }
        }

        /***************************************************************************/
        // 跳过string
        template<typename Archive>
        void json_skip_string(Archive& ar) {
            while (true) {
                const char ch = ar.getch();
                switch (ch) {
                case '\"': return;
                case '\\': {
                    const char ch2 = ar.getch();
                    switch (ch2) {
                    case 'u': json_skip_unicode(ar);
                    case '\"':
                    case '\\':
                    case '/':
                    case 'b':
                    case 'f':
                    case 'n':
                    case 'r':
                    case 't': continue;
                    default: __YAS_THROW_INVALID_JSON_STRING("invalid string: forbidden char")
                    }
                }
                default: continue;
                }
            }
        }

        /***************************************************************************/
        // 跳过数组
        template<typename Archive>
        void json_skip_array(Archive& ar) {
            while (true) {
                json_skipws(ar);
                if (ar.peekch() != ']')
                    json_skip_val(ar);

                json_skipws(ar);
                const char ch = ar.getch();
                if (ch == ']') break;
            }
        }

        /***************************************************************************/
        // 跳过对象
        template<typename Archive>
        void json_skip_object(Archive& ar) {
            while (true) {
                json_skipws(ar);
                if (ar.peekch() == '}') {
                    ar.getch();
                    break;
                }

                __YAS_THROW_IF_WRONG_JSON_CHARS(ar, "\"")

                    json_skip_string(ar); // key

                json_skipws(ar);
                __YAS_THROW_IF_WRONG_JSON_CHARS(ar, ":")

                    json_skipws(ar);
                json_skip_val(ar); // val

                json_skipws(ar);
                const char ch = ar.getch();
                if (ch == '}') break;
            }
        }

        /***************************************************************************/
        // 跳过value
        template<typename Archive>
        void json_skip_val(Archive& ar) {
            const char ch = ar.getch();
            switch (ch) {
            case '\"': { json_skip_string(ar); break; }
            case 't': { json_skip_bool('t', ar); break; }
            case 'f': { json_skip_bool('f', ar); break; }
            case 'n': { json_skip_null(ar); break; }
            case '[': { json_skip_array(ar); break; }
            case '{': { json_skip_object(ar); break; }
            case '-':
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9': { json_skip_num(ar); break; }
            default: break;
            }
        }

        /***************************************************************************/
        // 读取key
        template<typename Archive>
        std::size_t json_read_key(Archive& ar, char* ptr, std::size_t size) {
            const char* p = ptr;
            while (size--) {
                *ptr = ar.getch();
                if (*ptr == '\"') {
                    ar.ungetch(*ptr);
                    *ptr = 0;

                    break;
                }
                ++ptr;
            }
            *ptr = 0;

            return ptr - p;
        }

        /***************************************************************************/
        // 是否是合理的整型和双精度浮点型数
        template<typename Archive>
        bool is_valid_for_int_and_double(Archive&, char ch) {
            switch (ch) {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            case '-':
            case '.':
            case 'e':
            case 'E': return true;
            default: return false;
            }
        }

        // 读数字
        template<typename Archive>
        std::size_t json_read_num(Archive& ar, char* ptr, std::size_t size) {
            char* p = ptr;
            do {
                *ptr = ar.getch();
                switch (*ptr) {
                case '-':
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9': break;
                default: {
                    ar.ungetch(*ptr);
                    return ptr - p;
                }
                }
                ++ptr;
            } while (--size);

            return ptr - p;
        }

        template<typename Archive>
        std::size_t json_read_double(Archive& ar, char* ptr, std::size_t size) {
            char* p = ptr;
            do {
                *ptr = ar.getch();
                switch (*ptr) {
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                case '-':
                case '.':
                case 'e':
                case 'E': break;
                default: {
                    ar.ungetch(*ptr);
                    return ptr - p;
                }
                }
                ++ptr;
            } while (--size);

            return ptr - p;
        }

        /***************************************************************************/

    } // ns detail
} // ns yas

#endif // __yas__detail__tools__json_tools_hpp
